<?php
declare (strict_types=1);

namespace app\common\service;

use think\facade\Cache;
use app\common\enum\sms\SceneEnum;
use app\common\model\SettingModel;

class SmsCaptchaService extends BaseService
{
    // 最大发送次数，默认10次
    protected $sendTimes = 10;

    // 发送限制间隔时间，默认24小时
    protected $safeTime = 86400;

    // 发送短信
    public function sendCaptcha(string $mobile, string $scene, array $param): array
    {
        // 数据验证
        $this->validate(['mobile' => $mobile, 'scene' => $scene]);

        $cache_tag = "_cache_tag_captcha_sms_{$mobile}_{$scene}";

        if (Cache::get($cache_tag)) {
            return render_array_error('请不要频繁获取验证码');
        }

        // 获取验证码次数是否超限
        if (!$this->record($mobile)) {
            return render_array_error('很抱歉，已超出今日最大发送次数限制');
        }

        $sms_config = SettingService::getItem('sms');

        // 短信服务
        $scene_config = $sms_config['scene'][$scene];
        if (!$scene_config['is_enable']) {
            return render_array_error('短信通知服务未开启，请在后台中设置');
        }

        // 申明场景需要的变量
        extract($param);

        try {
            $content = $scene_config['content'];
            $templateParams = [];
            foreach ($scene_config['variable'] as $var) {
                $content = str_replace('${' . $var . '}', $$var, $content);
                $templateParams[$var] = $$var;
            }

            $SmsDriver = new \app\common\library\sms\Driver($sms_config);
            $SmsDriver->sendSms($mobile, $content, $scene_config['template'], $templateParams);
        } catch (\Exception $e) {
            return render_array_error($e->getMessage());
        }

        // 有验证码的场景写入缓存，用于后续验证
        switch ($scene) {
            case SceneEnum::LOGIN:
                Cache::set($cache_tag, $sms_code, 600);
                break;
        }

        return render_array_success();
    }

    // 记录短信验证码发送记录并判断是否超出发送限制
    private function record(string $mobile): bool
    {
        $cache_tag = "_cache_tag_captcha_sms_times_{$mobile}";

        // 获取发送记录缓存
        $record = Cache::get($cache_tag);

        // 写入缓存:记录剩余发送次数
        if (empty($record)) {
            Cache::set($cache_tag, $this->sendTimes - 1, $this->safeTime);
            return true;
        }

        // 判断发送次数是否合法
        if ($record <= 0) {
            return false;
        }
        // 发送次数递减
        Cache::dec($cache_tag);
        return true;
    }

    // 数据验证
    private function validate(array $data)
    {
        // 数据验证
        $rule = [
            'mobile' => 'require|mobile',
            'scene' => 'require|in:' . implode(',', array_keys(SceneEnum::data())),
        ];
        validate($rule)->check($data);
    }

    // 验证短信
    public static function checkSms(string $mobile, string $code, $scene): array
    {
        $cache_tag = "_cache_tag_captcha_sms_{$mobile}_{$scene}";

        $cache_sms_code = Cache::get($cache_tag);
        if (empty($cache_sms_code) || $cache_sms_code != $code) return render_array_error('验证码错误或过期');

        return render_array_success();
    }
}
